package xjay.ai.emotionalsupport.agent;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.ai.tool.ToolCallback;
import xjay.ai.emotionalsupport.chatmemory.ChatHistoryService;
import xjay.ai.emotionalsupport.tools.Markdown2TextTool;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Data
public abstract class CharacterBaseAgent extends ReActAgent{

    private  ToolCallback[] agentTools;
    private ToolCallingManager toolCallingManager; //ToolCallingManager 不需要知道所有工具，它只需要在执行时，从 ChatResponse 和 prompt 中获取必要的信息来完成调用

    private ChatResponse toolCallChatResponse;
    private final ChatOptions chatOptions;
    private ChatClient chatClient; //子类注入

    private ToolResponseMessage toolResponseMessage;

    private ChatHistoryService chatHistoryService;

    private Markdown2TextTool markdown2TextTool;

    public CharacterBaseAgent(ToolCallback[] agentTools) {
        super();
        this.agentTools = agentTools;

        //如果是阿里的Dashscope模型会失效
        chatOptions = ToolCallingChatOptions.builder()
                .internalToolExecutionEnabled(false)
                .build();
        toolCallingManager = ToolCallingManager.builder().build();


    }


    /**
     * note：不需要工具调用，则添加助手消息；需要工具调用，则在act中设置全部对话的上下文
     * @return
     */
    @Override
    public Boolean reasoning() {//决定要不要调用工具; 要展示思维链过程
        // 每轮推理开始时，清空上轮工具调用缓存，避免旧状态影响
        toolCallChatResponse = null;
        toolResponseMessage = null;
        // 拼接instructionPrompt与SystemPrompt作为系统提示词
        String combinedPrompt =getSystemPrompt() + "\n\n" + getInstructionPrompt();

        //拿到完整的聊天列表（因为是不依赖框架自己维护）

        List<Message> messageList = getMessageList();
        //使用聊天列表和聊天选项构建prompt
        Prompt prompt = new Prompt(messageList, this.chatOptions); // 使用消息列表和聊天选项构建 Prompt，目的在于关闭框架自动调用工具，和传入上下文

        //调用LLM,得到ChatResponse
        ChatResponse chatResponse = chatClient.prompt(prompt)//构建复杂prompt的入口
                .system(combinedPrompt)
                .toolCallbacks(agentTools)
                .call()
                .chatResponse();

        //保存think结果，以便act调用（类成员方法之间的通信是通过类成员字段进行的）
        toolCallChatResponse = chatResponse;

        //得到助手（模型）消息，一个是打印日志，一个是查看消息中是否有工具调用
        AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
        String result = assistantMessage.getText();
        List<AssistantMessage.ToolCall> toolCalls = assistantMessage.getToolCalls();
        log.info(getAgentName() + " resoning：" + result);


        if(toolCalls.isEmpty()) {
            log.info("toolCalls is empty");
            // 只有不调用工具时，才需要手动记录助手消息
            getMessageList().add(assistantMessage);
            return false;
        } else {
            log.info("reasoning tool calling: " + assistantMessage.getToolCalls());
            return true;
        }
    }

    /**
     * note: 设置了整个对话上下文
     */
    @Override
    public void act() {// 工具调用

        if(!toolCallChatResponse.hasToolCalls()) { //没有工具调用
            return;
        }

        Prompt prompt = new Prompt(getMessageList(), this.chatOptions); //通过new Prompt实例，控制ToolCallManager进行工具调用

        //核心1：程序控制框架进行工具调用，通过ToolCallingManager的executeToolCalls
        ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, toolCallChatResponse);

        //为什么toolExecutionResult会有一个对话历史，包含了整个对话历史
        setMessageList(toolExecutionResult.conversationHistory());

        //核心2：拿到工具调用返回的消息
        this.toolResponseMessage = (ToolResponseMessage) CollUtil.getLast(toolExecutionResult.conversationHistory());


    }

    @Override
    public String observe() {

        if(!toolCallChatResponse.hasToolCalls()) {
            return "没有工具调用";
        }

        //判断是否调用了终止工具
        boolean terminateToolCalled = toolResponseMessage.getResponses().stream()
                .anyMatch(response -> response.name().equals("taskTerminate")); //taskTerminate是方法
        if(terminateToolCalled) {
            setStatus(AgentStatus.FINISHED);
        }

        //拿到工具调用的结果，并格式化地输出字符串，以喂给llm
    String results = toolResponseMessage.getResponses().stream()
            .map(response -> {
                String toolName = response.name();
                String data = response.responseData();
                return String.format("通过%s获得信息：%s", toolName, data);
            })
            .collect(Collectors.joining("\n"));

        //输出日志
        log.info(results);
        return results;
    }

}
